#include "mycam.h"
#include "mymqtt.h"
#include "ArduinoJson.h"
#include "nvs_flash.h"
#include "nvs.h"
#include <OneWire.h>
#include <DallasTemperature.h>
// 定义串口
HardwareSerial mySerial(2);  // 串口2 (UART2)
extern HardwareSerial LDSerial;  // 串口1 (UART1)
// 定义MQTT账号信息
char deviceID[20] = "XBSJ20241216032";   // 可修改的设备ID XBSJ20241203001
unsigned long AIR780e_baud =3000000;     
const char* mqttUser = "mosquitto";  // 登录用户名
const char* mqttPassword = "hfut";  // 登录密码
const char* mqttHost = "8.136.109.126";  // MQTT服务器IP
int mqttPort = 1883;  // MQTT服务器端口
char mqttTopic[50];  // MQTT订阅
char MainBuffer[500]={0};
//************************************************************************************************//
uint32_t ConfigModuleNoBlockCnt =0;   //配置函数延时变量,定时器内部累加
int ConfigModuleNoBlockCaseValue = 0; //控制执行哪一条Case 语句
char ConfigModuleNoBlockFlage  = 0;   //1-配置完连接 0-未配置完连接

uint32_t CompareValue=0;                //每隔 Ms 发送一次数据
uint32_t SendNextDelay =0;                 //接收SendConfigFunction函数最后一个参数,最终传递给 ConfigModuleNoBlockCnt 控制写一条数据发送的时间
int Cnt = 0;                          //记录运行状态发送的次数
char DataReturnFlage  = 0;            //是否返回了预期的数据

char HopeReturnData1[20]="";//存储希望返回的数据
char HopeReturnData2[20]="";//存储希望返回的数据

void (*ConfigConnectDispose)(char *data,int len);//定义一个函数指针变量,用来处理模块返回的数据
void ConfigModuleRunNext(int delay);



uint32_t  MainLen;      
unsigned char *MainString;   
extern esp_err_t ret;
extern nvs_handle_t my_handle;
extern DallasTemperature sensors;
int64_t temperatureC;
extern int32_t boot_count;
#define SLEEP_TIME 240000  // 超时时间
#define SLEEP_CTL  5       //单位休眠时间
uint16_t rcount1=0;
uint16_t rcount2=0;
const unsigned long LDSerialtimeout = 1000;

;
// 定义雷达帧头和帧尾
const uint8_t frameHeader[] = {0xF4, 0xF3, 0xF2, 0xF1};
const uint8_t frameTail[] = {0xF8, 0xF7, 0xF6, 0xF5};

// 定义最大数据长度
const int MAX_DATA_LENGTH = 256;

// 存储接收到的数据
uint8_t receivedData[MAX_DATA_LENGTH];
// 当前接收到的数据长度
int dataLength = 0;
// 标记是否找到帧头
bool foundHeader = false;
// 标记是否找到完整的数据包
bool foundCompletePacket = false;

// MQTT任务函数
void MQTTTask(void *pvParameters) {



    Serial.println("MQTT started!") ;
    // 构建完整的 MQTT 主题
    snprintf(mqttTopic, sizeof(mqttTopic), "/vrf/%s", deviceID);
    Serial.println("mqttTopic:mqttTopic/set") ;

    while (1)
    {
		//作为延时时钟
		ConfigModuleNoBlockCnt+=10;
        ConfigMQTTNoBlock();
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    
}



/**
* @brief  发送指令配置模块,非阻塞版
* @waring 
* @param  None 
* @param  None
* @param  None
* @retval None
* @example 
**/
void ConfigMQTTNoBlock(void)
{
	
	if(ConfigModuleNoBlockCnt>CompareValue && ConfigModuleNoBlockFlage==0)
	{
		ConfigModuleNoBlockCnt=0;
		if(DataReturnFlage == 1)//上一条指令是OK的
		{
			Cnt=0;
			DataReturnFlage = 0;
			ConfigModuleNoBlockCaseValue ++;//执行下一条
		}
		else
		{
			Cnt ++;
			if(Cnt>=3)//超过3次重新执行
			{
				Cnt=0;
				ConfigModuleNoBlockCaseValue = 0;
			}
		}
		switch(ConfigModuleNoBlockCaseValue)
		{
			case 0://关闭GPRS模块
				CompareValue = 500;SendConfigFunction("AT+IPR=3000000;&W\r\n",NULL,"OK",NULL,FunctionParseCompare,CompareValue);break;
			case 1:
				CompareValue = 3500;SendConfigFunction("AT+RESET\r\n",NULL,NULL,NULL,NULL,CompareValue);DataReturnFlage=1;break;
			case 2://关闭GPRS模块
				CompareValue = 1000;SendConfigFunction("AT+CSQ\r\n",NULL,"OK",NULL,FunctionParseCompare,CompareValue);break;
			case 3://启动GPRS模块
				CompareValue = 1000;SendConfigFunction("AT+CCID\r\n",NULL,"OK",NULL,FunctionParseCompare,CompareValue);break;
			case 4://GPRS注册状态		
				CompareValue = 3000;SendConfigFunction("AT+CGREG?\r\n",NULL,"+CGREG: 0,1",NULL,FunctionParseCompare,CompareValue);break;
			case 5://是否附着了GPRS  
				CompareValue = 1000;SendConfigFunction("AT+CGATT?\r\n",NULL,"+CGATT: 1",NULL,FunctionParseCompare,CompareValue);break;			
			case 6://设置MQTT 账号密码
				MainLen = sprintf((char*)MainBuffer,"AT+MCONFIG=%s,%s,%s\r\n",deviceID,mqttUser,mqttPassword);
				MainBuffer[MainLen]=0;
				CompareValue = 1000;SendConfigFunction((char*)MainBuffer,NULL,"OK",NULL,FunctionParseCompare,CompareValue);
				break;
			case 7://连接MQTT 服务器 
				MainLen = sprintf((char*)MainBuffer,"AT+MIPSTART=%s,%d\r\n",mqttHost,mqttPort);
				MainBuffer[MainLen]=0;
				CompareValue =1000;SendConfigFunction((char*)MainBuffer,NULL,"CONNECT OK","ALREADY CONNECT",FunctionParseCompare,CompareValue);	
				break;	
			case 8://建立MQTT会话
				CompareValue = 1000;SendConfigFunction("AT+MCONNECT=1,60\r\n",NULL,"CONNACK OK",NULL,FunctionParseCompare,CompareValue);break;

			case 9://订阅 1
				CompareValue=1000;
				MainLen = sprintf((char*)MainBuffer,"AT+MSUB=\"%s/time\",1\r\n",mqttTopic);
				MainBuffer[MainLen]=0;
				SendConfigFunction((char*)MainBuffer,NULL,"SUBACK",NULL,FunctionParseCompare,CompareValue);		
				break;
			case 10://订阅完成后进行设备上报
			{

				temperpub();//温度发布
				int32_t number_read;
				ret = nvs_get_i32(my_handle, "number", &number_read);//读取图像频率
				if (ret!= ESP_OK) {
						number_read = 0;
				}
				if(boot_count>=number_read/5)//比较开机次数和图像间隔时间/5min
				{
					boot_count=0;
					LDpub();
					IRpub();	
					campub();//发布图像
				}
				Get_sleep_time();//休眠
			}
			break;

			default: 
				SendConfigFunction(NULL,NULL,NULL,NULL,NULL,CompareValue);//这句必须加,清除所有的执行函数
				DataReturnFlage=0; 
				ConfigModuleNoBlockFlage=0; break;
		}
	}
}


/**
* @brief  执行下一条
* @param  delay 延时多少时间再执行下一条
* @param  
* @retval 
* @example 
延时1000ms再执行下一条
ConfigModuleRunNext(CompareValue-1000);
立即执行下一条
ConfigModuleRunNext(CompareValue);
**/
void ConfigModuleRunNext(int delay){
	SendNextDelay = delay;
	DataReturnFlage=1;
	ConfigModuleNoBlockCnt = (SendNextDelay == 0 ? ConfigModuleNoBlockCnt:SendNextDelay);
}



/**
* @brief  发送配置指令函数
* @param  order          发送数据(字符串)
* @param  FunctionSend   发送数据(运行发送数据函数)
* @param  HopeReturn1    接收数据(希望返回的字符串1)
* @param  HopeReturn2    接收数据(希望返回的字符串2)
* @param  FunctionParse  数据处理函数
* @param  ConfigFunctionValue  控制下一条数据的发送时间,取值为 ConfigFunctionCntCom:下一条立即发送
* @retval 
* @example 
**/
void SendConfigFunction(char *order,void (*FunctionSend)(),char *HopeReturn1,char *HopeReturn2,void (*FunctionParse)(char *data,int len),uint32_t ConfigFunctionValue)
{
	memset(HopeReturnData1,NULL,strlen(HopeReturnData1));
	memset(HopeReturnData2,NULL,strlen(HopeReturnData2));
	if(HopeReturn1!=NULL)  sprintf(HopeReturnData1,"%s",HopeReturn1);//拷贝数据到数组 HopeReturn1,希望返回的数据1
	if(HopeReturn2!=NULL)  sprintf(HopeReturnData2,"%s",HopeReturn2);//拷贝数据到数组 HopeReturn1,希望返回的数据2
	if(FunctionSend!=NULL) FunctionSend();//调用一个函数发送指令
	
	ConfigConnectDispose = FunctionParse;//传递处理函数指针
	SendNextDelay = ConfigFunctionValue;//如果上一条数据处理成功,控制发送下一条数据的时间
	
	if(order!=NULL){
        mySerial.print(order);

	}
}


/**
* @brief  处理配置返回数据:判断是否返回想要的数据
* @param  data
* @param  
* @retval 
* @example 
**/
void FunctionParseCompare(char *data,int len)	
{
	if((strlen(HopeReturnData1) != 0 && strstr(data, HopeReturnData1)) || memcmp(HopeReturnData1, "\0", len) == 0 )
	{
		DataReturnFlage=1;
		ConfigModuleNoBlockCnt = (SendNextDelay == 0 ? ConfigModuleNoBlockCnt:SendNextDelay);
	}
	if((strlen(HopeReturnData2) != 0 && strstr(data, HopeReturnData2)) || memcmp(HopeReturnData1, "\0", len) == 0)
	{
		DataReturnFlage=1;
		ConfigModuleNoBlockCnt = (SendNextDelay == 0 ? ConfigModuleNoBlockCnt:SendNextDelay);
	}
}

void Get_sleep_time(void)
{

	ret = nvs_set_i32(my_handle, "boot_count", boot_count);
	if (ret != ESP_OK) {
	Serial.println("Error writing to NVS!");
	} else 
	{
		Serial.println("boot_count written to NVS successfully!");
	}
	// 提交更改
	ret = nvs_commit(my_handle);
	if (ret != ESP_OK) {
	Serial.println("Error committing changes to NVS!");
	} else {
	Serial.println("Changes committed to NVS successfully!");
	}
	uint64_t sleep_time_us =SLEEP_CTL* 1000000 * 60;				
	esp_sleep_enable_timer_wakeup(sleep_time_us);
	
	Serial.println("ESP32休眠...");	
	esp_deep_sleep_start();
}

// UART任务函数
void UARTTask(void *pvParameters) {
    String response = "";  // 用于存储完整的响应数据

	mySerial.begin(AIR780e_baud, SERIAL_8N1, 15, 16);  // 设置为使用 IO15 和 IO16

    while (true) {
        while (mySerial.available()) {
            // 读取一个字符
            char c = mySerial.read();
            // 将字符追加到响应字符串中
            response += c;
			
            // 如果读取到换行符，表示一行结束
            if (c == '\n') {

				if (response.startsWith("+MSUB:")) {
				// 提取主题和消息长度
					int firstComma = response.indexOf(',');
					int secondComma = response.indexOf(',', firstComma + 1);
					String topic = response.substring(7, firstComma); // 跳过"+MSUB:"部分
					int msgLength = response.substring(firstComma + 1, secondComma).toInt();
					String jsonPayload = response.substring(secondComma + 1);

					//   // 打印提取的信息
					//   Serial.println("Topic: " + topic);
					//   Serial.println("Message Length: " + String(msgLength));
					//   Serial.println("JSON Payload: " + jsonPayload);
					StaticJsonDocument<200> doc;
					DeserializationError error = deserializeJson(doc, jsonPayload);


					// 获取JSON字段值
					cam_time = doc["time"].as<int>();
					ret = nvs_set_i32(my_handle, "number", cam_time);
					if (ret != ESP_OK) {
					Serial.println("Error writing to NVS!");
					} else {
						Serial.println("Number written to NVS successfully!");
					}

					// 提交更改
					ret = nvs_commit(my_handle);
					if (ret != ESP_OK) {
						Serial.println("Error committing changes to NVS!");
					} else {
						Serial.println("Changes committed to NVS successfully!");
					}
					//Serial.println("Time: " + String(timeValue));
					
					}

                // 若ConfigConnectDispose函数指针非空，则调用处理函数
                if(response.length() >= 2 && response.length() <=200)
                {
                    //处理回复
                    if (ConfigConnectDispose != NULL ) {
                        // 将响应数据转换为C字符串
                        char *dtmp = (char*)response.c_str();
                        int pos = response.length();

                        // 调用处理函数
                        ConfigConnectDispose(dtmp, pos);
						Serial.print(response);
                    }
                    // 调用提取和调整时间的函数
                }
				
                // 清空响应字符串，以便处理下一行
                response = "";
            }
        }

        // 任务延时以释放CPU资源
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

void checkSleepTask(void *pvParameters) {
    unsigned long startTime = millis(); // 记录启动时间

    while (true) {
        unsigned long currentTime = millis(); // 获取当前时间

        // 检查从启动以来是否已过去 60 秒
        if (currentTime - startTime >= SLEEP_TIME) {

            Get_sleep_time();
        } 
        // 每隔 1000 毫秒检查一次睡眠条件
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}


void temperpub(void)
{
   sensors.requestTemperatures(); 
  // 获取第一个传感器（如果只有一个DS18B20连接）的温度值，单位为摄氏度
  temperatureC = sensors.getTempCByIndex(0);
  vTaskDelay(pdMS_TO_TICKS(1000));

//   if (temperatureC == -127.00) {

//     Serial.println("Error: Could not read temperature from DS18B20 sensor");

//   } else {
//     Serial.print("Temperature: ");
//     Serial.print(temperatureC);
//     Serial.println(" °C");
// }
	String publishCmd;
	publishCmd += String("AT+MPUB=\"")  + "/vrf/temp\",0,0,"+"\"{\\0D\\0A\\22deviceid\\22"+":\\22"+String(deviceID)+"\\22"+",\\0D\\0A\\22temp\\22"+":\\22"+String(temperatureC)+"\\22\\0D\\0A"+"}\"\r\n";

	SendConfigFunction((char*)publishCmd.c_str(), NULL, "OK",NULL, FunctionParseCompare, CompareValue);
	vTaskDelay(pdMS_TO_TICKS(100));
	if(DataReturnFlage==1)
	{
		DataReturnFlage=0;
		
	}
}

void campub(void)
{
	         

				vTaskDelete(captureTask_handle);
				captureTask_handle=NULL;
				fb = esp_camera_fb_get();

				if (!fb) {  
					//Serial.println("Failed to capture image");
					String publishCmd2;
					publishCmd2 += String("AT+MPUB=\"") + mqttTopic + "/post/cam?" +"\",0," +"0,"+"\"{\\0D\\0A\\22type\\22"+":\\22"+"err\\22";
					publishCmd2 +="\\22\\0D\\0A";
					publishCmd2 +="}\"\r\n";
					SendConfigFunction((char*)publishCmd2.c_str(), NULL, "OK",NULL, FunctionParseCompare, CompareValue);//发送图像块
					vTaskDelay(pdMS_TO_TICKS(200));	

				}
				else{
					//Serial.println("suc to capture image");
					String publishCmd2;
					publishCmd2 += String("AT+MPUB=\"") + mqttTopic + "/post/cam?" +"\",0," +"0,"+"\"{\\0D\\0A\\22type\\22"+":\\22"+"suc\\22";
					publishCmd2 +="\\22\\0D\\0A";
					publishCmd2 +="}\"\r\n";
					SendConfigFunction((char*)publishCmd2.c_str(), NULL, "OK",NULL, FunctionParseCompare, CompareValue);//发送图像块
					vTaskDelay(pdMS_TO_TICKS(200));	
					DataReturnFlage=0;

				const size_t chunkSize =2000; // 每次发送的最大字节数，可以根据需要调整
				size_t totalChunks = (fb->len + chunkSize - 1) / chunkSize; // 总块数
				for (size_t i = 0; i <= totalChunks+2; i++) 
				{
					String publishCmd;
					if(i<totalChunks)
					{						
						size_t offset = i * chunkSize;
						size_t remainingBytes = fb->len - offset;
	
						// 确定当前块的大小
						size_t currentChunkSize = remainingBytes > chunkSize ? chunkSize : remainingBytes;

						//Serial.printf("图像剩余大小: %u 字节\n", remainingBytes);
						publishCmd += String("AT+MPUB=\"") + mqttTopic + "/post/chunk" + String(i) + "\",0," +"0,"+"\"{\\0D\\0A\\22type\\22"+":\\22"+"1\\22"+",\\0D\\0A\\22str\\22"+":\\22"; 
						for (size_t j = 0; j < currentChunkSize; j++) {
							char hexBuffer[3]; // 用于存储两个字符和结束符
							sprintf(hexBuffer, "%02X", fb->buf[offset + j]); // 将字节转换为两位十六进制数
							publishCmd +=hexBuffer;     	// 将JPEG数据附加到命令中。
						}
						
					}
					else{
						publishCmd += String("AT+MPUB=\"") + mqttTopic + "/post/chunk" + String(totalChunks) + "\",0," +"0,"+"\"{\\0D\\0A\\22type\\22"+":\\22"+"0\\22"+",\\0D\\0A\\22str\\22"+":\\22"; 
					}

					publishCmd +="\\22\\0D\\0A";
					publishCmd +="}\"\r\n";	

					SendConfigFunction((char*)publishCmd.c_str(), NULL, "+CME ERROR: 767",NULL, FunctionParseCompare, CompareValue);//发送图像块
					vTaskDelay(pdMS_TO_TICKS(200));	//每包间隔


					if(DataReturnFlage==1)//收到+CME ERROR: 767重发前3包
					{	
						Serial.print("错误:chunk");
						Serial.println(i);
						if(i<3)i=3;
						Serial.print("重发:chunk");
						Serial.println(i-3);
						DataReturnFlage=0;
						i-=4;
						start:
						rcount1=0;
						rcount2=0;


						SendConfigFunction("AT+MDISCONNECT\r\n",NULL,"OK",NULL,FunctionParseCompare,CompareValue);
						vTaskDelay(pdMS_TO_TICKS(500));
						DataReturnFlage=0;

						SendConfigFunction("AT+MIPCLOSE\r\n",NULL,"OK",NULL,FunctionParseCompare,CompareValue);
						vTaskDelay(pdMS_TO_TICKS(500));
						DataReturnFlage=0;
						
						MainLen = sprintf((char*)MainBuffer,"AT+MIPSTART=%s,%d\r\n",mqttHost,mqttPort);
						MainBuffer[MainLen]=0;
						SendConfigFunction((char*)MainBuffer,NULL,"CONNECT OK","ALREADY CONNECT",FunctionParseCompare,CompareValue);
						
						while(DataReturnFlage==0)
						{
						 vTaskDelay(pdMS_TO_TICKS(100));
						 rcount1++;
						 if(rcount1>30)
						 {
							rcount1=0;
							goto start;
						 }
						}
						DataReturnFlage=0;
						SendConfigFunction("AT+MCONNECT=1,60\r\n",NULL,"CONNACK OK",NULL,FunctionParseCompare,CompareValue);

						while(DataReturnFlage==0)
						{
        				 vTaskDelay(pdMS_TO_TICKS(100));
						 rcount2++;
						 if(rcount2>30)
						 {
							
							goto start;
						 }

						}
						DataReturnFlage=0;
						vTaskDelay(pdMS_TO_TICKS(100));
						
					}

				}
				esp_camera_fb_return(fb);                	// 返回帧缓冲区以供后续使用.
				//vTaskDelay(pdMS_TO_TICKS(2000)); 
				}
			  
}

bool findHeader(void) {
	if (LDSerial.available() >= 4) {
	  uint8_t buffer[4];
	  LDSerial.readBytes(buffer, 4);
	  if (buffer[0] == frameHeader[0] && buffer[1] == frameHeader[1] &&
		  buffer[2] == frameHeader[2] && buffer[3] == frameHeader[3]) {
		// 找到帧头，将帧头数据存入 receivedData
		memcpy(receivedData, buffer, 4);
		dataLength = 4;
		foundHeader = true;
		return true;
	  }
	  // 未找到帧头，将读取的数据丢弃一个字节，继续查找
	  for (int i = 0; i < 3; i++) {
		buffer[i] = buffer[i + 1];
	  }
	  LDSerial.readBytes(buffer + 3, 1);
	  if (buffer[0] == frameHeader[0] && buffer[1] == frameHeader[1] &&
		  buffer[2] == frameHeader[2] && buffer[3] == frameHeader[3]) {
		memcpy(receivedData, buffer, 4);
		dataLength = 4;
		foundHeader = true;
		return true;
	  }
	}
	return false;
  }
  
  // 查找帧尾
bool findTail(void) {
	if (dataLength >= 4) {
	  uint8_t* lastFourBytes = receivedData + dataLength - 4;
	  if (lastFourBytes[0] == frameTail[0] && lastFourBytes[1] == frameTail[1] &&
		  lastFourBytes[2] == frameTail[2] && lastFourBytes[3] == frameTail[3]) {
		foundCompletePacket = true;
		return true;
	  }
	}
	return false;
  }
void LDpub(void)
{

	unsigned long startTime = millis();
	while (millis()-startTime<3000)
	{
		if (!foundHeader) {
			// 未找到帧头，继续查找
			findHeader();
		  } else {
			// 找到帧头，开始接收数据
			if (LDSerial.available() > 0) {
			  uint8_t byte = LDSerial.read();
			  if (dataLength < MAX_DATA_LENGTH) {
				receivedData[dataLength++] = byte;
				// 检查是否找到帧尾
				findTail();
			  }
			}
		  }
		
		  if (foundCompletePacket) {
			String publishCmd;
			String LDCmd;
			for (size_t j = 0; j < dataLength; j++) {
				char hexBuffer[3]; // 用于存储两个字符和结束符
				sprintf(hexBuffer, "%02X", receivedData[j]); // 将字节转换为两位十六进制数
				LDCmd +=hexBuffer;     	
			}
			publishCmd += String("AT+MPUB=\"")  + "/vrf/LD\",0,0,"+"\"{\\0D\\0A\\22deviceid\\22"+":\\22"+String(deviceID)+"\\22"+",\\0D\\0A\\22LD\\22"+":\\22"+LDCmd+"\\22\\0D\\0A"+"}\"\r\n";
			SendConfigFunction((char*)publishCmd.c_str(), NULL, "OK",NULL, FunctionParseCompare, CompareValue);
			vTaskDelay(pdMS_TO_TICKS(100));
			if(DataReturnFlage==1)DataReturnFlage=0;
			break;
			dataLength = 0;
			foundHeader = false;
			foundCompletePacket = false;
		  }

	}
	

}
void IRpub(void)
{
	int pinState = digitalRead(IR_PIN);
	String publishCmd;
	publishCmd += String("AT+MPUB=\"")  + "/vrf/IR\",0,0,"+"\"{\\0D\\0A\\22deviceid\\22"+":\\22"+String(deviceID)+"\\22"+",\\0D\\0A\\22IR\\22"+":\\22"+String(pinState)+"\\22\\0D\\0A"+"}\"\r\n";
	SendConfigFunction((char*)publishCmd.c_str(), NULL, "OK",NULL, FunctionParseCompare, CompareValue);
	vTaskDelay(pdMS_TO_TICKS(100));
	if(DataReturnFlage==1)DataReturnFlage=0;
}